using System;
using System.Collections.Generic;
using System.IO;
using SCG.SolarVengeanceEngine.BrainInfo;
using SCG.SolarVengeanceEngine.StarShips;
using SCG.General;

namespace SCG.SolarVengeanceEngine
{
    public abstract class Brain
    {
        //public members

        //constructor
        public Brain()
        {
        }

        //Get the name of the Brain
        public string BrainName
        {
            get
            {
                return GetType().Name;
            }
        }

        //difficulty level
        public abstract ChallengeLevel Challenge
        {
            get;
        }

        //rating
        public double Rating { get; set; }

        //development mode?
        public virtual bool DevelopmentMode
        {
            get
            {
                return false;
            }
        }

        //Let the Brain do its thing!
        public abstract void Execute();

        //Provide a description of the Brain
        public abstract string Description
        {
            get;
        }

        //Author
        public virtual string Author
        {
            get
            {
                return "SCG";
            }
        }

        //Respond to incoming chat
        public virtual void IncomingMessage(string playerName, string message)
        {
        }

        //Return a list of Ship types that the brain will use
        public virtual List<StarShipType> AvailableShipTypes
        {
            get
            {
                List<StarShipType> ships = new List<StarShipType>();
                ships.Add(StarShipType.WarShip);
                ships.Add(StarShipType.ScanShip);
                ships.Add(StarShipType.Drone);
                ships.Add(StarShipType.EMine);
                ships.Add(StarShipType.Freighter);
                ships.Add(StarShipType.SpyShip);
                return ships;
            }
        }

        //Get/restore Brain Data
        public virtual string GetBrainData()
        {
            return "";
        }
        public virtual void ParseBrainData(string brainData)
        {
        }

        //used for brain duel matches
        public int MatchInfo { get; set; }

        //The Brain API

        //Access info about player
        protected string Name
        {
            get
            {
                return ThisPlayer.Name;
            }
        }

        //Access info about other players
        protected IList<string> PlayerNames
        {
            get
            {
                return _playerNames;
            }
        }
        protected bool IsPlayerHuman(string playerName)
        {
            Player p = ThisGame.FindPlayer(playerName);
            if (p == null)
                return false;
            return p.Brain == null;
        }

        //Multiplayer game?
        protected bool IsMultiPlayerGame
        {
            get
            {
                return ThisGame.IsMultiPlayer;
            }
        }

        //Easy access to their capital
        protected StarSystemInfo Capital
        {
            get
            {
                if (ThisPlayer.Capital == null)
                    return null;
                return ThisPlayer.Capital.Info;
            }
        }

        //Access tech multiple
        protected double TechMultiple
        {
            get
            {
                return ThisPlayer.TechMultiple;
            }
        }

        //Dimentions of map
        protected int MapWidth
        {
            get
            {
                return ThisGame.Width;
            }
        }
        protected int MapHeight
        {
            get
            {
                return ThisGame.Height;
            }
        }

        //Get info on terrain
        protected TerrainType GetTerrain(int x, int y)
        {
            char c = ThisGame.GetTerrain(x, y);
            if (c == ' ')
                return TerrainType.Clear;
            else
                return TerrainType.Nebula;
        }

        //get object at specific coordinated
        protected SVObjectInfo GetObjectAt(int x, int y)
        {
            if (x < 0)
                return null;
            if (y < 0)
                return null;
            if (x >= MapWidth)
                return null;
            if (y >= MapHeight)
                return null;
            SVMapObject svm = ThisGame.GetObjectAt(x, y);
            if (svm is StarShip)
            {
                StarShip ship = svm as StarShip;
                return ship.Info;
            }
            else if (svm is StarSystem)
            {
                StarSystem sys = svm as StarSystem;
                return sys.Info;
            }
            else
                return null;
        }

        //Access StarSystems
        protected IList<StarSystemInfo> StarSystems
        {
            get
            {
                return _systems;
            }
        }
        protected internal StarSystemInfo FindStarSystem(string name)
        {
            foreach (StarSystemInfo sys in StarSystems)
                if (sys.Name == name)
                    return sys;
            return null;
        }

        //Access StarShips
        protected IList<StarShipInfo> StarShips
        {
            get
            {
                return _ships;
            }
        }
        protected IList<StarShipInfo> MyStarShips
        {
            get
            {
                return _myShips;
            }
        }
        protected internal StarShipInfo FindStarShip(string code)
        {
            foreach (StarShipInfo ship in MyStarShips)
                if (ship.Code == code)
                    return ship;
            foreach (StarShipInfo ship in StarShips)
                if (ship.Code == code)
                    return ship;
            return null;
        }

        //Access Wormholes
        protected IList<WormholeInfo> Wormholes
        {
            get
            {
                return _wormholes;
            }
        }

        //Access Black Holes
        protected IList<BlackHoleInfo> BlackHoles
        {
            get
            {
                return _blackHoles;
            }
        }

        //Access the Pulsars
        protected IList<PulsarInfo> Pulsars
        {
            get
            {
                return _pulsars;
            }
        }

        //Access UDOs
        protected IList<UDOInfo> UDOs
        {
            get
            {
                return _udos;
            }
        }

        //Access AllianceLevel
        protected AllianceLevel AlliedWith(string playerName)
        {
            Player p = ThisGame.FindPlayer(playerName);
            if (p == null)
                return AllianceLevel.None;
            return ThisPlayer.AlliedWith(p);
        }
        protected AllianceLevel AlliedWithMe(string playerName)
        {
            Player p = ThisGame.FindPlayer(playerName);
            if (p == null)
                return AllianceLevel.None;
            return p.AlliedWith(ThisPlayer);
        }

        //Set an alliance
        protected void SetAlliance(string playerName, AllianceLevel al)
        {
            if (playerName == ThisPlayer.Name)
                return;
            Player p = ThisGame.FindPlayer(playerName);
            if (p == null)
                return;
            SubmitOrder(OrderType.Alliance, ThisPlayer, p, al);          
        }

        //Communicate with a player
        protected void SendMessage(ChatChannel channel, string msg)
        {
            if (ThisGame.Host != null)
                ThisGame.Host.BrainSendChat(ThisPlayer, channel, msg);
        }

        //StarSystem commands

        //Build Shields
        protected void BuildShields(StarSystemInfo system, int number)
        {
            if (!system.OwnedByMe)
                return;
            int resource = system.Resources;
            int shields = system.Shields;
            while (number > 0 && resource > shields)
            {
                SubmitOrder(OrderType.BuildShield, system._system);                 
                resource -= shields;
                shields++;
                number--;
            }
        }
        protected void SetAutoShields(StarSystemInfo system, bool autoShieldsOn)
        {
            if (!system.OwnedByMe)
                return;
            if (autoShieldsOn)
                SubmitOrder(OrderType.AutoShields, system._system, true);
            else
                SubmitOrder(OrderType.AutoShields, system._system, false);         
        }

        //Build Scanners
        protected void BuildScanners(StarSystemInfo system, int number)
        {
            if (!system.OwnedByMe)
                return;
            int resource = system.Resources;
            int scanners = system.Scanners;
            while (number > 0 && resource > scanners)
            {
                SubmitOrder(OrderType.BuildScanner, system._system);            
                resource -= scanners;
                scanners++;
                number--;
            }
        }
        protected void SetAutoScanners(StarSystemInfo system, bool autoScannersOn)
        {
            if (!system.OwnedByMe)
                return;
            if (autoScannersOn)
                SubmitOrder(OrderType.AutoScanners, system._system, true);
            else
                SubmitOrder(OrderType.AutoScanners, system._system, false);          
        }

        //Build a StarShip
        protected void BuildStarShip(StarSystemInfo system, StarShipType shipType, int engines, int value, bool cloaked)
        {
            if (!ThisPlayer.ShipTypesAvailable.Contains(shipType))
                return;
            StarSystem sys = system._system;
            if (sys.Owner == ThisPlayer)
            {
                if (engines < 1)
                    engines = 1;
                if (engines > 20)
                    engines = 20;
                if (value < 1)
                    value = 1;
                if (value > 200)
                    value = 200;
                int cost = ShipCostCalculator.GetCost(shipType, engines, value, cloaked, sys);
                if (cost <= sys.ResourcesInt)
                    SubmitOrder(OrderType.BuildStarShipAutoPos, sys, shipType, engines, value, cloaked);                          
            }
        }
        protected void SetAutoStarShip(StarSystemInfo system, bool autoStarShipOn)
        {
            if (!system.OwnedByMe)
                return;
            if (autoStarShipOn)
                SubmitOrder(OrderType.AutoStarShip, system._system, true);
            else
                SubmitOrder(OrderType.AutoStarShip, system._system, false);            
        }
        protected void SetAutoStarShipType(StarSystemInfo system, StarShipType shipType, int engines, int value, bool cloaked)
        {
            if (!system.OwnedByMe)
                return;
            SubmitOrder(OrderType.SetAutoBuildType, system._system, new StarShipDescriptor(shipType, engines, value, cloaked));
            SubmitOrder(OrderType.AutoStarShip, system._system, true);
        }

        //StarShip commands

        //clear order
        protected void ClearOrder(StarShipInfo ship)
        {
            SubmitOrder(OrderType.ClearOrders, ship._ship);
        }

        //Move
        protected void Move(StarShipInfo ship, int x, int y)
        {
            if (x < 0)
                x = 0;
            if (x >= ThisGame.Width)
                x = ThisGame.Width - 1;
            if (y < 0)
                y = 0;
            if (y >= ThisGame.Height)
                y = ThisGame.Height;         
            if (!ship.OwnedByMe)
                return;
            if (x == ship.X && y == ship.Y)
                return;
            SubmitOrder(OrderType.Move, ship._ship, x, y);         
        }

        //Set RemoteViewer target space
        protected void SetRemoteViewPoint(StarShipInfo ship, int x, int y)
        {
            if (!ship.OwnedByMe)
                return;
            if (ship._ship.CanExecuteOrder(OrderType.SetViewPoint))
                return;
            if (x < 0)
                x = 0;
            if (x >= ThisGame.Width)
                x = ThisGame.Width - 1;
            if (y < 0)
                y = 0;
            if (y >= ThisGame.Height)
                y = ThisGame.Height;
            SubmitOrder(OrderType.SetViewPoint, ship._ship, x, y);
        }

        //Defend StarSystem
        protected void Defend(StarShipInfo ship, StarSystemInfo system)
        {
            if (!ship.OwnedByMe)
                return;
            if (!ship._ship.CanExecuteOrder(OrderType.Defend))
                return;
            SubmitOrder(OrderType.Defend, ship._ship, system._system);
        }

        //Defend StarShip
        protected void Defend(StarShipInfo ship, StarShipInfo defendShip)
        {
            if (!ship.OwnedByMe)
                return;
            if (!ship._ship.CanExecuteOrder(OrderType.Defend))
                return;
            SubmitOrder(OrderType.Defend, ship._ship, defendShip._ship);
        }

        //Defend a point
        protected void Defend(StarShipInfo ship, SCGPoint pt)
        {
            if (!ship.OwnedByMe)
                return;
            if (!ship._ship.CanExecuteOrder(OrderType.Defend))
                return;
            SubmitOrder(OrderType.DefendPoint, ship._ship, (int)pt.X, (int)pt.Y);
        }

        //Attack adjacent on/off
        protected void AttackAdjacent(StarShipInfo ship, bool attack)
        {
            if (!ship.OwnedByMe)
                return;
            StarShip ss = ship._ship;
            if (ss.CanExecuteOrder(OrderType.AttackAdjacent))
                SubmitOrder(OrderType.AttackAdjacent, ss, attack);          
        }

        //Target a StarSystem
        protected void Target(StarShipInfo ship, StarSystemInfo system)
        {
            if (!ship.OwnedByMe)
                return;
            StarShip ss = ship._ship;
            StarSystem sys = system._system;
            if (ss.CanTargetStarSystem(sys, false))
                SubmitOrder(OrderType.TargetStarSystem, ship._ship, system._system);
        }

        //Target a StarShip
        protected void Target(StarShipInfo ship, StarShipInfo target)
        {
            if (!ship.OwnedByMe)
                return;
            StarShip ss = ship._ship;
            StarShip ssTarget = target._ship;
            if (ss.CanTargetStarShip(ssTarget, false))
                SubmitOrder(OrderType.TargetStarShip, ship._ship, target._ship);           
        }

        //Escort a StarShip
        protected void Escort(StarShipInfo ship, StarShipInfo escortTarget, int offsetX, int offsetY)
        {
            if (!ship.OwnedByMe)
                return;
            StarShip ss = ship._ship;
            StarShip escort = escortTarget._ship;
            SubmitOrder(OrderType.Escort, ship._ship, escortTarget._ship, offsetX, offsetY);        
        }

        //Release a StarShip/StarSystem
        protected void Release(StarShipInfo ship)
        {
            if (!ship.OwnedByMe)
                return;
            if (ship.ShipType == StarShipType.Tug)
            {
                Tug tug = ship._ship as Tug;
                if (tug.Towing != null)
                    SubmitOrder(OrderType.TugRelease, ship._ship);
            }
            else if (ship.ShipType == StarShipType.GravShip)
            {
                GravShip grav = ship._ship as GravShip;
                if (grav.Dragging != null)
                    SubmitOrder(OrderType.GravRelease, ship._ship);
            }
        }

        //Transport
        protected void Transport(StarShipInfo ship, StarSystemInfo from, StarSystemInfo to)
        {
            if (!ship.OwnedByMe)
                return;
            if (ship.ShipType != StarShipType.Freighter)
                return;
            if (from.OwnedByMe && to.OwnedByMe)
                SubmitOrder(OrderType.Transport, ship._ship, to._system, 1, from._system);            
        }
        protected void TransportBS(StarShipInfo ship, StarShipInfo battleStation, StarSystemInfo to)
        {
            if (!ship.OwnedByMe)
                return;
            if (ship.ShipType != StarShipType.Freighter)
                return;
            if (battleStation._ship.Owner.FriendlyTo(ship._ship.Owner) && to.OwnedByMe)
                SubmitOrder(OrderType.Transport, ship._ship, to._system, 1, battleStation._ship);
        }
        protected void Transport2(StarShipInfo ship, List<SVObjectInfo> sources, SVObjectInfo destination)
        {
            if (!ship.OwnedByMe)
                return;
            if (ship.ShipType != StarShipType.Freighter)
                return;
            if (!ValidateObject(destination))
                return;
            List<SVMapObject> objects = new List<SVMapObject>();
            foreach (SVObjectInfo svo in sources)
            {
                if (!ValidateObject(svo))
                    return;
                else
                    objects.Add(svo.MapObject);
            }
            //Construct a Transport order and submit it
            SVTokenizer tok = new SVTokenizer(ThisGame);
            tok.AppendToken(OrderType.Transport);
            tok.AppendToken(ship._ship);
            tok.AppendToken(destination.MapObject);
            tok.AppendToken(objects.Count);
            foreach (object obj in objects)
                tok.AppendToken(obj);
            _orders.Add(tok.Result);   
        }
        protected void Unload(StarShipInfo ship, SVObjectInfo destination)
        {
            if (!ship.OwnedByMe)
                return;
            if (ship.ShipType != StarShipType.Freighter)
                return;
            if (!ValidateObject(destination))
                return;
            SubmitOrder(OrderType.Unload, ship._ship, destination.MapObject);
        }

        private bool ValidateObject(SVObjectInfo svo)
        {
            if (svo is StarShipInfo)
            {
                StarShipInfo ssi = svo as StarShipInfo;
                return ssi._ship.Owner.FriendlyTo(Brain.ThisPlayer);
            }
            else
            {
                StarSystemInfo ssi = svo as StarSystemInfo;
                return ssi._system.Owner.FriendlyTo(Brain.ThisPlayer);
            }
        }

        //Create JumpGate
        protected void CreateJumpGate(StarShipInfo ship)
        {
            if (!ship.OwnedByMe)
                return;
            if (ship.ShipType == StarShipType.JumpGateShip)
            {
                JumpGateShip jgs = ship._ship as JumpGateShip;
                StarSystem origin = jgs.OriginStarSystem;
                if (origin == null || origin.JumpGateCreated)
                    return;
                SubmitOrder(OrderType.CreateGate, jgs);             
            }
        }

        //Detonate MadBomb
        protected void DetonateMadBomb(StarShipInfo ship)
        {
            if (!ship.IdentifiedByMe)
                return;
            if (ship.ShipType == StarShipType.MadBomb)
                SubmitOrder(OrderType.Detonate, ship._ship);
        }

        //BS Build StarShip
        protected void BuildStarShip(StarShipInfo ship, StarShipType shipType, int engines, int value, bool cloaked)
        {
            if (!ThisPlayer.ShipTypesAvailable.Contains(shipType))
                return;
            StarShip s = ship._ship;
            if (!s.CanExecuteOrder(OrderType.ShipBuildStarShip))
                return;
            if (s.Owner == ThisPlayer)
            {
                ResourceStarShip rss = s as ResourceStarShip;
                if (engines < 1)
                    engines = 1;
                if (engines > 20)
                    engines = 20;
                if (value < 1)
                    value = 1;
                if (value > 10000)
                    value = 10000;
                int cost = ShipCostCalculator.GetCost(shipType, engines, value, cloaked, null);
                if (cost <= rss.ResourcesInt)
                    SubmitOrder(OrderType.ShipBuildStarShipAutoPos, rss, shipType, engines, value, cloaked);                          
            }
        }

        //Eject UDOs
        protected void EjectUDOs(StarShipInfo ship)
        {
            StarShip s = ship._ship;
            if (s.Owner == ThisPlayer)
                SubmitOrder(OrderType.EjectUDO, ship._ship);            
        }

        //Misc methods

        //General distance
        protected double Distance(int x1, int y1, int x2, int y2)
        {
            double diffX = (x1 - x2);
            double diffY = (y1 - y2);
            if (diffX == 0 && diffY == 0)
                return 0;
            return Math.Sqrt(diffX * diffX + diffY * diffY);
        }
        protected double Distance(double x1, double y1, double x2, double y2)
        {
            return Distance((int)x1, (int)y1, (int)x2, (int)y2);
        }
        protected int DistanceQuick(int x1, int y1, int x2, int y2)
        {
            int diffX = Math.Abs(x1 - x2);
            int diffY = Math.Abs(y1 - y2);
            return diffX > diffY ? diffX : diffY;          
        }
        protected int DistanceQuick(double x1, double y1, double x2, double y2)
        {
            return DistanceQuick((int)x1, (int)y1, (int)x2, (int)y2);
        }

        //Random range
        protected int Random(int from, int to)
        {
            return SVGame.Random(from, to);
        }

        //Closest StarSystem
        protected StarSystemInfo ClosestStarSystem(int x, int y, int maxDistance, FindSystemFlags findType)
        {
            StarSystemInfo si = null;
            double closest = Double.MaxValue;
            foreach (StarSystemInfo sys in StarSystems)
            {
                bool include = false;
                if ((findType & FindSystemFlags.Owned) > 0)
                    include = sys.OwnedByMe;
                if (!include)
                    if ((findType & FindSystemFlags.Independent) > 0)
                        if (sys.Visible)
                            if (sys.Owner == "")
                                include = true;
                if (!include)
                    if ((findType & FindSystemFlags.NonVisible) > 0)
                        if (!sys.Visible)
                            include = true;
                if (!include)
                    if ((findType & FindSystemFlags.Allied) > 0)
                        if (sys.Visible && sys.Owner != "" && !sys.OwnedByMe)
                            if (AlliedWith(sys.Owner) > AllianceLevel.None)
                                include = true;
                if (!include)
                    if ((findType & FindSystemFlags.Enemy) > 0)
                        if (sys.Visible && sys.Owner != "" && !sys.OwnedByMe)
                            if (AlliedWith(sys.Owner) == AllianceLevel.None)
                                include = true;
                if (!include)
                    continue;

                double dist = DistanceQuick(sys.X, sys.Y, x, y);
                if (dist < closest && dist <= maxDistance)
                {
                    closest = dist;
                    si = sys;
                }
            }
            return si;
        }

        //Closest StarShip
        protected StarShipInfo ClosestStarShip(int x, int y, int maxDistance, FindShipFlags findType, ShipKnownFlags known)
        {
            StarShipInfo si = null;
            double closest = Double.MaxValue;
            foreach (StarShipInfo ship in StarShips)
            {
                bool includeKnown = (known & ShipKnownFlags.Known) > 0;
                bool includeUnknown = (known & ShipKnownFlags.Unknown) > 0;
                if (!includeKnown)
                    if (ship.IdentifiedByMe)
                        continue;
                if (!includeUnknown)
                    if (!ship.IdentifiedByMe)
                        continue;

                bool include = false;
                if ((findType & FindShipFlags.Owned) > 0)
                    include = ship.OwnedByMe;
                if (!include)
                    if ((findType & FindShipFlags.Enemy) > 0)
                        if (!ship.OwnedByMe && AlliedWith(ship.Owner) == AllianceLevel.None)
                            include = true;
                if (!include)
                    if ((findType & FindShipFlags.Allied) > 0)
                        if (!ship.OwnedByMe && AlliedWith(ship.Owner) > AllianceLevel.None)
                            include = true;
                if (!include)
                    continue;

                double dist = DistanceQuick(ship.X, ship.Y, x, y);
                if (dist < closest && dist <= maxDistance)
                {
                    closest = dist;
                    si = ship;
                }
            }
            return si;
        }

        //closest pulsar
        public PulsarInfo ClosestPulsar(int x, int y, int maxDistance)
        {
            PulsarInfo pi = null;
            double minDist = Double.MaxValue;
            foreach (Pulsar p in ThisGame.Pulsars)
            {
                double d = Distance(x, y, p.X, p.Y);
                if (d < minDist)
                    if (d <= maxDistance)
                    {
                        minDist = d;
                        pi = p.Info;
                    }
            }
            return pi;
        }

        //private members
        internal static Player ThisPlayer = null;
        internal static SVGame ThisGame = null;
        internal static Brain ThisBrain = null;
        private ISVHost _host;      
        private List<StarSystemInfo> _systems = new List<StarSystemInfo>();
        private List<StarShipInfo> _ships = new List<StarShipInfo>();
        private List<StarShipInfo> _myShips = new List<StarShipInfo>();
        private List<string> _orders = new List<string>();
        private List<WormholeInfo> _wormholes = new List<WormholeInfo>();
        private List<BlackHoleInfo> _blackHoles = new List<BlackHoleInfo>();
        private List<PulsarInfo> _pulsars = new List<PulsarInfo>();
        private List<string> _playerNames = new List<string>();
        private List<UDOInfo> _udos = new List<UDOInfo>();

        //Submit an order
        private void SubmitOrder(OrderType ot, params object[] parameters)
        {
            SVTokenizer tok = new SVTokenizer(ThisGame);
            tok.AppendToken(ot);
            foreach (object obj in parameters)
                tok.AppendToken(obj);
            _orders.Add(tok.Result);
        }

        //Just initialize, so data can be assigned for AvailableShiptypes
        internal void Initialize(SVGame game, Player player)
        {
            ThisGame = game;
            ThisPlayer = player;
            ThisBrain = this;       

            //StarSystems - reloading each time on off chance that scenarios will allow creation/destruction of them
            _systems.Clear();
            foreach (StarSystem system in game.StarSystems)
                _systems.Add(system.Info);

            //StarShips, load visible ones, and owned ones into separate list
            _ships.Clear();
            _myShips.Clear();
            foreach (StarShip ship in game.StarShips)
            {
                if (ship.Owner == player)
                    _myShips.Add(ship.Info);
                if (ship.VisibleTo[player])
                    _ships.Add(ship.Info);
            }

            //Wormholes
            _wormholes.Clear();
            foreach (Wormhole wh in game.Wormholes)
                if (wh.DiscoveredBy[player])
                    _wormholes.Add(wh.Info);

            //Black Holes
            _blackHoles.Clear();
            foreach (BlackHole bh in game.BlackHoles)
                if (bh.DiscoveredBy[player])
                    _blackHoles.Add(bh.Info);

            //Pulsars
            _pulsars.Clear();
            foreach (Pulsar ps in game.Pulsars)
                if (ps.DiscoveredBy[player])
                    _pulsars.Add(ps.Info);

            //Player names
            _playerNames.Clear();
            foreach (Player p in game.Players)
                if (!p.Eliminated)
                    _playerNames.Add(p.Name);

            //UDOs
            _udos.Clear();
            foreach (UDO udo in game.UDOs)
                if (udo.VisibleTo[player])
                    _udos.Add(udo.Info);
        }

        //Initialize data for a new processing impulse
        internal List<string> InitializeAndExecute(SVGame game, Player player, ISVHost host)
        {            
            Initialize(game, player);
            _orders.Clear();
            _host = host;

            //Execute the brain-specific logic
            SVObjectInfo.BrainExecuting = this;
            Execute();

            //Return the orders
            return _orders;
        }
    }

    //Type of system/ship to find?
    [Flags]
    public enum FindSystemFlags
    {
        Owned = 1, Independent = 2, NonVisible = 4, Allied = 8, Enemy = 16,
        All = Owned + Independent + NonVisible + Allied + Enemy,
        Visible = Owned + Independent + Allied + Enemy,
        Friendly = Owned + Allied,
        Unfriendly = Independent + NonVisible + Enemy,
        VisibleUnfriendly = Independent + Enemy
    };

    [Flags]
    public enum FindShipFlags
    {
        Owned = 1, Allied = 2, Enemy = 4,
        Friendly = Owned + Allied     
    }

    [Flags]
    public enum ShipKnownFlags
    {
        Known = 1, Unknown = 2, Either = Known + Unknown
    }

    public enum ChallengeLevel { Training, Easy, Moderate, Difficult, Extreme }
}
